package net.java.nioserver.aio;

import net.java.nioserver.IoLogUtil;
import net.java.nioserver.operations.IoEventListener;
import net.java.nioserver.utils.ChannelUtil;
import net.jcip.annotations.NotThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

/**
 * @author Leonid Shlyapnikov
 */
@NotThreadSafe
class AioEventListenerTask implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(AioEventListenerTask.class);

    private final IoEventListener listener;
    private SocketChannel channel;
    private final SelectionKey key;

    AioEventListenerTask(IoEventListener listener,
                         SocketChannel channel, SelectionKey key) {
        this.listener = listener;
        this.channel = channel;
        this.key = key;
    }

    public void run() {
        try {
            boolean runMore = listener.handle(channel);
            if (!runMore) {
                closeChannelFarEndDisconnected();
            }
        } catch (InterruptedException e) {
            log.info("Interruption requested", e);
            ChannelUtil.closeChannelCancelKey(key);
            Thread.currentThread().interrupt();
        } catch (AsynchronousCloseException e) {
            IoLogUtil.logAsynchronousCloseException(e, channel, log);
        } catch (ClosedChannelException e) {
            IoLogUtil.logClosedChannelException(e, channel, log);
            ChannelUtil.closeChannelCancelKey(key);
        } catch (IOException e) {
            IoLogUtil.logIOException(e, listener, channel, log);
            ChannelUtil.closeChannelCancelKey(key);
        } catch (Exception e) {
            ChannelUtil.closeChannelCancelKey(key);
            throw new IllegalStateException("Unexpected exception dispatching event to: " + listener
                    + ", channel closed", e);
        }
    }

    private void closeChannelFarEndDisconnected() {
        if (log.isInfoEnabled()) {
            log.info("Far end disconnected, closing channel: " + channel);
        }
        ChannelUtil.closeChannelCancelKey(key);
    }
}
